package cn.edu.hitsz.compiler.lexer;

import cn.edu.hitsz.compiler.NotImplementedException;
import cn.edu.hitsz.compiler.symtab.SymbolTable;
import cn.edu.hitsz.compiler.utils.FileUtils;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.stream.StreamSupport;

/**
 * TODO: 实验一: 实现词法分析
 * <br>
 * 你可能需要参考的框架代码如下:
 *
 * @see Token 词法单元的实现
 * @see TokenKind 词法单元类型的实现
 */
public class LexicalAnalyzer {
    private final SymbolTable symbolTable;
    private String file_path;

    public LexicalAnalyzer(SymbolTable symbolTable) {
        this.symbolTable = symbolTable;
    }

    private ArrayList<Token> tokens;

    private boolean isInt(String c){
        Pattern pattern = Pattern.compile("[0-9]");
        return pattern.matcher(c).matches();
    }

    public static boolean isLetter(String input) {
        String regex = "[A-Za-z]"; // 此处使用的正则表达式模式为只包含字母的字符串
        return input.matches(regex);
    }

    public static boolean isSin(String input) {

        char res = input.charAt(0);
        return (res == '+' || res == '-' || res == '*' || res == '/');
    }


   /**
     * 从给予的路径中读取并加载文件内容
     *
     * @param path 路径
     */
    public void loadFile(String path) {
        // TODO: 词法分析前的缓冲区实现
        // 可自由实现各类缓冲区
        // 或直接采用完整读入方法
        file_path = path;
    }

    /**
     * 执行词法分析, 准备好用于返回的 token 列表 <br>
     * 需要维护实验一所需的符号表条目, 而得在语法分析中才能确定的符号表条目的成员可以先设置为 null
     */
    public void run() throws FileNotFoundException {
        // TODO: 自动机实现的词法分析过程
        try (InputStream fd = new FileInputStream(file_path)){

                int b = fd.read();
                String single_char = new String(String.valueOf(b));
            while(true){
                if(b == -1){
                    break;
                }
                if(b == ' ' || b == '\n' || b == '\t'){
                    continue;
                }

                String res = "";
                
                if(isInt(single_char)){
                    
                    res += single_char;
                    b = fd.read();
                    single_char = new String(String.valueOf(b));

                    while(isInt(single_char)){
                        res += single_char;
                        b = fd.read();
                        single_char = new String(String.valueOf(b));
                    }

                    tokens.add(Token.normal(TokenKind.fromString(res), res));
                    continue;

                } else if (isLetter(single_char)) {
                    
                    res += single_char;
                    
                    b = fd.read();
                    single_char = new String(String.valueOf(b));

                    while(isInt(single_char) || isLetter(single_char)){
                        res += single_char;
                        b = fd.read();
                        single_char = new String(String.valueOf(b));
                    }

                    tokens.add(Token.normal(TokenKind.fromString(res), res));
                    continue;

                } else if (single_char == "*"){

                    res += single_char;
                    b = fd.read();
                    single_char = new String(String.valueOf(b));
                    if(single_char == "*"){
                        
                        res += single_char;
                        tokens.add(Token.simple(TokenKind.fromString(res))); 

                        b = fd.read();
                        single_char = new String(String.valueOf(b));
                        continue;
                    }else{
                        tokens.add(Token.simple(TokenKind.fromString(res))); 
                        continue;
                    }
                    
                    
                } else if (single_char == "="){

                    res += single_char;
                    b = fd.read();
                    single_char = new String(String.valueOf(b));
                    if(single_char == "="){
                        
                        res += single_char;
                        tokens.add(Token.simple(TokenKind.fromString(res))); 

                        b = fd.read();
                        single_char = new String(String.valueOf(b));
                        continue;
                    }else{
                        tokens.add(Token.simple(TokenKind.fromString(res))); 
                        continue;
                    }
                
                }else if (single_char == "\""){

                    res += single_char;
                    b = fd.read();
                    single_char = new String(String.valueOf(b));
                    while(isInt(single_char) || isLetter(single_char)){
                        res += single_char;
                        b = fd.read();
                        single_char = new String(String.valueOf(b));
                    }
                       
                    res += single_char;
                    b = fd.read();
                    single_char = new String(String.valueOf(b));
                    tokens.add(Token.normal(TokenKind.fromString(res), res));
                    continue;
                }else if (single_char == "(" || single_char == ")" || single_char == ":"
                        || single_char == "+" || single_char == "-" || single_char == "'"
                        || single_char == "//" ){

                    res += single_char;
                    b = fd.read();
                    single_char = new String(String.valueOf(b));
                    tokens.add(Token.simple(TokenKind.fromString(res)));
                    continue;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获得词法分析的结果, 保证在调用了 run 方法之后调用
     *
     * @return Token 列表
     */
    public Iterable<Token> getTokens() {
        // TODO: 从词法分析过程中获取 Token 列表
        // 词法分析过程可以使用 Stream 或 Iterator 实现按需分析
        // 亦可以直接分析完整个文件
        // 总之实现过程能转化为一列表即可
        return tokens;
    }

    public void dumpTokens(String path) {
        FileUtils.writeLines(
            path,
            StreamSupport.stream(getTokens().spliterator(), false).map(Token::toString).toList()
        );
    }


}
